package tij4.concurrent.exercise.ex36;

import org.omg.CORBA.INTERNAL;
import tij4.concurrent.source.Course;
import tij4.concurrent.source.Food;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.*;
/*
    暂时先不考虑了!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
 */
/*
    桌子类中应该拥有什么对象?
    什么对象中应该拥有桌子类?
 */

/*
    饭店业务流程:
        顾客点餐--->服务员生成订单--->厨师拿到订单--->厨师做好菜放到盘子里--->服务员端盘子--->客户拿到菜--->客户吃菜
        名词,固有的,不是动作
            顾客,服务员,订单,厨师,菜,盘子,饭店
            做成类

            订单:--被动
                顾客
                服务员
                食物
            顾客:--被动
                多个盘子
                <服务员> -- 要用服务员里面的方法
            服务员:--主动
                --多个客户-- 没有此属性是直接从饭店里的订单里调用的客户里面的方法
                饭店
                <多个盘子> -- 要用顾客里面的方法
            食物:--被动
            盘子:--被动
                食物
                订单:盘子参与业务流程是靠订单维护的
            厨师:--被动
                饭店:没有必须的属性,但是要参与饭店流程,
            饭店:被动
                实
                    多个服务员
                    多个厨师
                虚
                    多个订单
*/

/*
    为什么客户拥有服务员属性?
        点菜用到了服务员的方法 下单,
    为什么服务员没有拥有客户的属性?
        服务员也用到了顾客里面的方法但是是用过饭店调用的
 */
/*
    加个桌子,客户坐在桌子上
    饭店业务流程:
        服务员给客户安排桌子--->顾客点餐--->服务员生成订单--->厨师拿到订单--->厨师做好菜放到盘子里--->服务员端盘子--->客户拿到菜--->客户吃菜
        名词,固有的,不是动作
            顾客,服务员,订单,厨师,菜,盘子,饭店,桌子(限定人数)
            做成类
        桌子类分析:可以让订单里包含桌子,顾客通过订单和桌子联系起来!!!


        顾客,服务员,订单,厨师,菜,盘子,饭店
        做成类

        桌子
            订单(要求)
            多个顾客
        订单:--被动
            顾客
            服务员
            食物
            桌子
        顾客:--被动
            多个盘子
            <服务员> -- 要用服务员里面的方法
        服务员:--主动
            --多个客户-- 没有此属性是直接从饭店里的订单里调用的客户里面的方法
            饭店
            <多个盘子> -- 要用顾客里面的方法
        食物:--被动
        盘子:--被动
            食物
            订单:盘子参与业务流程是靠订单维护的
        厨师:--被动
            饭店:没有必须的属性,但是要参与饭店流程,
        饭店:被动
            实
                多个服务员
                多个厨师
            虚
                多个订单
*/

/*
    桌子,限定客户的人数,一般为4个,由服务员指定
    桌子上有顾客,有菜,还和服务员有关系,还属于一个订单
 */
class Table {
    private static int counter = 0;
    private final int id = counter++;
    private ArrayBlockingQueue<Customer1> customers = new ArrayBlockingQueue<>(4);

    /*
        入座
     */
    public boolean addCustomer(Customer1 customer) {
        return customers.offer(customer);
    }
    /*

     */

    @Override
    public String toString() {
        return "Table " + id;
    }
}
/*
    订单
        订单没有盘子?
            盘子本来就不属于订单,是上菜的时候随机取得

        订单要记录客户,记录服务员,记录菜,
            订单有必要记录桌子吗?可以通过客户,找到桌子!!!那岂不是客户要拥有桌子属性
            桌子是要拥有客户的,这样才不会,坐不下了,还想着安排客户,

 */
class OrderTicket {
    private static int counter = 0;
    private final int id = counter++;
    private final Customer1 customer;
    private final WaitPerson1 waitPerson;
    private final Food food;

    public OrderTicket(Customer1 customer, WaitPerson1 waitPerson, Food food) {
        this.customer = customer;
        this.waitPerson = waitPerson;
        this.food = food;
    }
    public Food item() {
        return food;
    }

    public Customer1 getCustomer() {
        return customer;
    }

    public WaitPerson1 getWaitPerson() {
        return waitPerson;
    }

    @Override
    public String toString() {
        return "OrderTicket: " + id + " item: " + food
                + " for : " + customer + "served by: " + waitPerson;
    }
}

/*
    碟子
        盘子里有订单,和食物
            本来食物就应该放到盘子里,
            订单里没有指定盘子,如果盘子里不包含订单,那么就不知道食物应该上给谁!
            盘子是订单的容器,盘子是菜的容器
 */
class Plate {
    private final OrderTicket order;
    private final Food food;

    public Plate(OrderTicket order, Food food) {
        this.order = order;
        this.food = food;
    }
    public Food getFood() {
        return food;
    }
    public OrderTicket getOrder() {
        return order;
    }
    @Override
    public String toString() {
        return food.toString();
    }
}

/*
    客户:客户得有食物的属性吧,他点的菜,他要吃菜,有盘子属性,里面有菜和订单
 */
class Customer1 implements Runnable {
    private static int counter = 0;
    private final int id = counter++;
    private final WaitPerson1 waitPerson;   //为什么客户拥有服务员属性
//    private final Table table;              //要有地方做呀
    //一次只能接受一次
    private SynchronousQueue<Plate> placeSetting = new SynchronousQueue<>();

    public Customer1(WaitPerson1 waitPerson) {
        this.waitPerson = waitPerson;
    }
    public void deliver(Plate p) throws InterruptedException {  //菜来了
        //如果客户在在吃之前的则会阻塞
        placeSetting.put(p);
    }

    @Override
    public void run() {
        for (Course course : Course.values()) {
            Food food = course.randomSelection();
            try {
                waitPerson.placeOrderTicket(this, food);  //客户点菜
                //点菜用到了服务员的方法 下单,
                System.out.println(this + "eating  " + placeSetting.take());    //客户吃菜
            }catch (InterruptedException e) {
                System.out.println(this + "waiting for " + course + " interrupted");
            }
        }
        System.out.println(this + "finished meal, leaving");
    }

    @Override
    public String toString() {
        return "Customer " + id + " ";
    }
}

/*
    服务员
 */
class WaitPerson1 implements Runnable {
    private static int counter;
    private final int id = counter++;
    private final Restaurant1 restaurant;
    BlockingQueue<Plate> filledOrderTickets = new LinkedBlockingQueue<>();

    public WaitPerson1(Restaurant1 restaurant) {
        this.restaurant = restaurant;
    }
    public void placeOrderTicket(Customer1 customer, Food food) { //客户点菜
        try {
            restaurant.orders.put(new OrderTicket(customer, this, food)); //服务员生成订单
        }catch (InterruptedException e) {
            System.out.println(this + " placeOrderTicket interrupted");
        }
    }

    @Override
    public void run() {
        try {
            while (!Thread.interrupted()) {
                Plate plate = filledOrderTickets.take();  //服务员得到菜
                System.out.println(this + "recevice " + plate + " delivering to " +
                        plate.getOrder().getCustomer());
                plate.getOrder().getCustomer().deliver(plate);  //put 给客户上菜
                //服务员也用到了顾客里面的方法但是是用过饭店调用的

            }
        }catch (InterruptedException e) {
            System.out.println(this + " interrupted");
        }
        System.out.println(this + " off duty");
    }

    @Override
    public String toString() {
        return "WaitPerson " + id + " ";
    }
}

/*
    做菜
 */
class Chef1 implements Runnable {
    private static int counter = 0;
    private final int id = counter++;
    private final Restaurant1 restaurant;
    private static Random random = new Random(47);

    public Chef1(Restaurant1 restaurant) {
        this.restaurant = restaurant;
    }

    @Override
    public void run() {
        try {
            while (!Thread.interrupted()) {
                OrderTicket order = restaurant.orders.take(); //厨师拿到订单
                Food requestedItem = order.item();
                TimeUnit.MILLISECONDS.sleep(random.nextInt(500));
                Plate plate = new Plate(order, requestedItem);
                order.getWaitPerson().filledOrderTickets.put(plate);  //厨师给服务器菜
            }
        }catch (InterruptedException e) {
            System.out.println(this + " interrupted");
        }
        System.out.println(this + " off duty");
    }

    @Override
    public String toString() {
        return "Chef1 " + id + " ";
    }
}

/*
    餐馆
 */
class Restaurant1 implements Runnable {
    private List<WaitPerson1> waitPersonList = new ArrayList<>();
    private List<Chef1> chefList = new ArrayList<>();
    private ExecutorService executorService;
    private static Random random = new Random(47);
    BlockingQueue<OrderTicket> orders = new LinkedBlockingQueue<>();
    private Table[] tables;

    public Restaurant1(ExecutorService executorService, int nWaitPersons, int nChefs, int nTable) {
        this.executorService = executorService;
        for (int i = 0; i < nWaitPersons; i++) {
            WaitPerson1 waitPerson = new WaitPerson1(this);
            waitPersonList.add(waitPerson);
            executorService.execute(waitPerson);
        }
        for (int i = 0; i < nChefs; i++) {
            Chef1 chef = new Chef1(this);
            chefList.add(chef);
            executorService.execute(chef);
        }
        tables = new Table[nTable];
    }

    @Override
    public void run() {
        try {
            while (!Thread.interrupted()) {
                WaitPerson1 waitPerson1 = waitPersonList.get(random.nextInt(waitPersonList.size()));
                Customer1 customer = new Customer1(waitPerson1);
                executorService.execute(customer);
                TimeUnit.MILLISECONDS.sleep(100);
            }
        }catch (InterruptedException e) {
            System.out.println("Restaurant interrupted");
        }
        System.out.println("Restaurant closing");
    }
}
public class RestaurantWithQueues2 {
    public static void main(String[] args) throws Exception {
        ExecutorService executorService = Executors.newCachedThreadPool();
//        Restaurant1 restaurant = new Restaurant1(executorService, 5, 2);
//        executorService.execute(restaurant);
        TimeUnit.SECONDS.sleep(5);
        executorService.shutdownNow();
    }
}
